package com.daniel.create.singleton;

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Description 双重校验锁-线程安全，uniqueInstance 只需要被实例化一次，之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行，只有当 uniqueInstance 没有被实例化时，才需要进行加锁。双重校验锁先判断 uniqueInstance 是否已经被实例化，如果没有被实例化，那么才对实例化语句进行加锁。
 * @Author GanShaoXi
 * @ClassName SingletonPattern_1
 * @Return com.daniel.create.singleton
 * @Since 1.0.0
 * @Created on 2023/5/16 19:43
 */
public class SingletonPattern_4 {

    /*使用 volatile 可以禁止 JVM 的指令重排，保证在多线程环境下也能正常运行*/
    private static volatile SingletonPattern_4 uniqueInstance;

    String flag;

    private SingletonPattern_4(String flag) {
        this.flag = flag;
    }

    /**
     * 获取单例
     * 考虑下面的实现，也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下，如果两个线程同时执行 if 语句，
     * 那么两个线程就会同时进入 if 语句块内。虽然在 if 语句块内有加锁操作，但是两个线程都会执行 uniqueInstance = new Singleton();
     * 这条语句，只是先后的问题，那么就会进行两次实例化，从而产生了两个实例。因此必须使用双重校验锁，也就是需要使用两个 if 语句。
     *
     * @return
     */
    public static SingletonPattern_4 getUniqueInstance() {
        if (uniqueInstance == null) {
            synchronized (SingletonPattern_4.class) {
                System.out.println(Thread.currentThread().getName() + " start instantiation");
                // if (uniqueInstance == null) {
                uniqueInstance = new SingletonPattern_4(Thread.currentThread().getName());
                // }
            }
        }
        return uniqueInstance;
    }

    /**
     * 测试证明只有当getUniqueInstance方法中的if未被注释时才能保证多线程创建的是一个实例，否则会创建多个实例
     *
     * @param args
     */
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0, 5, 5, TimeUnit.SECONDS, new SynchronousQueue<>(), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "singleton_task_pool" + r.hashCode());
            }
        });

        extracted(threadPoolExecutor);
        extracted(threadPoolExecutor);
        extracted(threadPoolExecutor);
        extracted(threadPoolExecutor);
        extracted(threadPoolExecutor);


    }

    private static void extracted(ThreadPoolExecutor threadPoolExecutor) {
        threadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                SingletonPattern_4 singletonPattern4 = SingletonPattern_4.getUniqueInstance();
                System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName() + " The instance created by the current thread is:" + singletonPattern4.flag);
            }
        });
    }
}
